package com.ruoyi.material.service.impl;

import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.material.domain.*;
import com.ruoyi.material.domain.constant.FileConstant;
import com.ruoyi.material.domain.constant.FileTypeEnum;
import com.ruoyi.material.service.*;
import com.ruoyi.material.util.CompressUtils;

import com.ruoyi.material.util.FileUtil;
import com.ruoyi.material.util.RandomlyGeneratedUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;

import java.util.*;

/**
 * @program: MMS
 * @description: 文件上传下载服务
 * @author: cyw
 * @create: 2021-07-23 11:23
 **/
@Service
public class BaseUpAndLoadServiceImpl implements IBaseUpAndDownLoadService {
    Logger log = LoggerFactory.getLogger(BaseUpAndLoadServiceImpl.class);
    @Autowired
    private IBaseTagFileService baseTagFileService;
    @Autowired
    private IBaseLabelService baseLabelService;
    @Autowired
    private IBaseFileLabelService baseFileLabelService;
    @Autowired
    private IBaseFlieRecordService baseFlieRecordService;
    @Autowired
    private IBaseMaterialService baseMaterialService;
    //文件的访问路径
    private String accessPath = "";
    // 文件上传路径
//    private String saveFilePath = "";
    // 当前操作人

    @Override
    public boolean uploadZipFile(MultipartFile zipFile, String code,String createBy,String labs) throws IOException {
//        String[] labels = label.split(",");
        Map<Integer,Integer> labelMap=new HashMap<>();
        if(!labs.trim().equals("")){
            String[] labels = labs.split(",");
            for (int i = 0; i < labels.length; ) {
                labelMap.put(Integer.parseInt(labels[i + 1]), Integer.parseInt(labels[i]));
                i += 2;
            }
        }
        accessPath = saveFile(zipFile,code,createBy);
//        //把文件保存到本地
        String saveFilePath = getLocalPath(accessPath);
        log.debug("压缩文件上传路径 [{}]",saveFilePath);
        //解压文件并记录文件夹
        String unzipFilePath = saveFilePath.substring(0, saveFilePath.length() - ".zip".length());
        unzipFile(saveFilePath, unzipFilePath);

//        // 文件夹路径的映射
        Map<String,String> pathMap = saveDir(zipFile,code,createBy,unzipFilePath);
        log.debug("文件夹路径的映射【{}】" ,pathMap);
        log.debug("文件解压后的路径【{}】" ,unzipFilePath);

//        //保存文件
        return saveFileToDatabase(unzipFilePath,createBy,pathMap,labelMap);
    }
    /**
     * 文件夹保存到数据库
     */
    private Map<String,String> saveDir(MultipartFile zipFile, String code,String createBy,String unzipFilePath){
        // 文件夹名称
        String dirName = zipFile.getOriginalFilename().substring(0,zipFile.getOriginalFilename().length()-".zip".length());
//        log.debug("文件夹保存到数据库 文件名称【{}】",dirName);
        //创建上传文件夹的目录
        code = insertFileRecord(dirName,FileConstant.FOLDER_TYPE, 0L,code,createBy);
//        log.debug("文件夹保存到数据库 保存的目录标识【{}】",code);
        // 文件物理路径路径 与 数据库中目录标识的映射
        Map<String,String> pathMap = new HashMap<String,String>(){};
        pathMap.put(getAccessPath(unzipFilePath),code);
        return pathMap;
    }
    /**
     * 保存文件到服务器
     *
     * @param file
     * @param pcode 上传文件所在目录的标识码
     * @return 文件的访问路径
     */
    private String saveFile(MultipartFile file,String pcode,String createBy) throws IOException {
        String uploadPath = RuoYiConfig.getUploadPath();
//        log.debug("上传文件根路径 [{}]",uploadPath);
//        log.debug("上传文件名称[{}]",file.getName());
//        log.debug("上传文件原始名称名称[{}]",file.getOriginalFilename());
        String accessPath = FileUploadUtils.upload(uploadPath, file);
        //insertFileRecord( file.getOriginalFilename(),file.getContentType(),file.getSize(), pcode, createBy,accessPath.substring(Constants.RESOURCE_PREFIX.length(),accessPath.length()));
//        log.debug("BaseUpAndLoadServiceImpl.saveFile() 上传后访问的路径： [{}] ", accessPath);
        return accessPath;
    }

    /**
     * 把文件插入到数据库
     * @param file
     * @param type
     * @param pcode
     * @param createBy
     * @param accessPath
     */
    private String  saveFileRecord(File file,String type,String pcode,String createBy,String accessPath){
        return insertFileRecord(file.getName(),type,file.length(),pcode,createBy,accessPath);
    }
    /**
     * 在数据库中插入一条文件记录
     */
    private String insertFileRecord( String fileName,String fileType,Long fileSize,String pcode,String createBy,String accessPath){
        log.debug("文件名称 【{}】",fileName);
        log.debug("文件pcode 【{}】",pcode);
        BaseFlieRecord record = new BaseFlieRecord();
        record.setFileSize(fileSize)
                .setOriginalFileName(fileName)
                .setPathFileName(Constants.RESOURCE_PREFIX+accessPath)
                .setContentType(fileType)
                .setCreateBy(createBy);
        record.setCreateTime(DateUtils.getNowDate());
        // todo  id 文件ID自定义
        baseFlieRecordService.insertBaseFlieRecord(record);
        record = baseFlieRecordService.selectBaseFlieRecordList(record).get(0);
        BaseMaterial material = new BaseMaterial();
        material.setpCode(pcode);
        material.setFileCode(record.getId());
        material.setName(fileName);
        material.setType(fileType);
        material.setCode(RandomlyGeneratedUtil.GeneratID());
        material.setCreateTime(record.getCreateTime());
        material.setId(material.getCode());
        material.setFileCode(record.getId());
        material.setCreateBy(createBy);
        material.setCreateTime(DateUtils.getNowDate());
        material.setDelFlag("0");
        baseMaterialService.insertBaseMaterial(material);

        return material.getCode();
    }

    /**
     * 在数据库中插入一条文件目录记录
     * @param fileName
     * @param fileType
     * @param fileSize
     * @param pcode
     * @param createBy
     * @return  创建目录的标识符
     */
    private String insertFileRecord( String fileName,String fileType,Long fileSize,String pcode,String createBy){
        log.debug("目录名称 【{}】",fileName);
        BaseFlieRecord record = new BaseFlieRecord();
        record.setFileSize(fileSize)
                .setOriginalFileName(fileName)
                .setPathFileName(null)
                .setContentType(fileType)
                .setUpdateBy(createBy);
        record.setCreateTime(DateUtils.getNowDate());
        //
        baseFlieRecordService.insertBaseFlieRecord(record);
        List<BaseFlieRecord> tempList = baseFlieRecordService.selectBaseFlieRecordList(record);
        record = tempList.get(tempList.size()-1);
        log.debug("todo {}" ,tempList);
        record = tempList.get(tempList.size()-1);
        BaseMaterial material = new BaseMaterial();
        material.setpCode(pcode);
        material.setName(fileName);
        material.setType(fileType);
        material.setCode(RandomlyGeneratedUtil.GeneratID());
        material.setCreateTime(record.getCreateTime());
        material.setId(material.getCode());
        material.setFileCode(record.getId());
        baseMaterialService.insertBaseMaterial(material);
        return material.getCode();
    }
    /**
     * 在数据库中插入一条文件记录
     */

    // todo 转换出现问题
    /**
     * 获取文件的本地地址
     *
     * @param path 访问路径
     * @return 物理地址
     */
    public String getLocalPath(String path) {
        // 本地资源路径
        String localPath = RuoYiConfig.getProfile();
        // 数据库资源地址
        String realFilePath = localPath + StringUtils.substringAfter(path, Constants.RESOURCE_PREFIX);
//        log.debug("BaseUpAndLoadServiceImpl.getRealFilePath() 上传后的访问路径： [{}] ", realFilePath);
        return realFilePath;
    }

    /**
     * 解压zip文件到指定文件夹
     *
     * @param zipFilePath 压缩文件地址
     * @param targetPath  解压后文件地址
     * @return
     */
    private void unzipFile(String zipFilePath, String targetPath) throws IOException {
//        log.debug("BaseUpAndLoadServiceImpl.unzipFile 解压zip文件到指定文件夹  form [{}] to [{}]", zipFilePath, targetPath);
        // 创建文件夹
        FileUtil.createDir(targetPath);
        CompressUtils.unzip(zipFilePath, targetPath);
    }

    /**
     * 把目录下的文件保存到数据库
     *
     * @param saveFilePath 目录的地址
     */
    private boolean saveFileToDatabase(String saveFilePath,String createBy,Map<String,String> pathMap,Map<Integer,Integer> labelMap) throws IOException {

//        List<BaseLabel> baseLabels = checkLabelNames(labels,createBy);

        Map<String, String> fileMap = new HashMap<>();
        Map<String,String> tagMap = new HashMap<>();
        StringBuffer curDir = new StringBuffer();
        curDir.append(saveFilePath);
        DfsDirData(saveFilePath,createBy,fileMap,tagMap,pathMap);
        linkedTagToRecord(tagMap ,fileMap,createBy);

        linkedLabelToRecord(labelMap,fileMap);
        return true;
    }
    /**
     * 素材与标签建立联系
     */
    public void linkedLabelToRecord( Map<Integer,Integer> labelMap,Map<String,String> fileMap){
        Set<String> fileKeySet = fileMap.keySet();
        List<BaseFileLabel> fileLabels = new ArrayList<>();
        for(String key:fileKeySet){
            BaseMaterial filMaterial = baseMaterialService.selectBaseMaterialById(fileMap.get(key));
            for (Integer labelCode:labelMap.keySet()) {
                System.out.println("x=="+labelCode+"  value"+labelMap.get(labelCode));
                BaseFileLabel baseFileLabel=new BaseFileLabel();
                baseFileLabel.setFileCode(filMaterial.getFileCode().longValue());
                baseFileLabel.setLabelCode(labelCode.longValue());
                baseFileLabelService.insertBaseFileLabel(baseFileLabel);
            }
        }
        log.debug("素材与标签建立联系 "+fileLabels.toString());
//        baseFileLabelService.addBaseFileLabelList(fileLabels);
    }
    /**
     * 让目录中的标记文件与文件关联
     * @param tagMap
     * @param fileMap
     */
    public void linkedTagToRecord(Map<String,String> tagMap,Map<String,String> fileMap,String createBy){
        Set<String> fileKeySet = fileMap.keySet();
        for(String key:fileKeySet){
            String tagCode = tagMap.get(key);
            if (tagCode != null && tagCode != ""){
                log.debug("匹配到标记文件 {} {}",tagCode,key );
                String fileCode = fileMap.get(key);

                if (!StringUtils.isEmpty(fileCode)){
                    try {
                        log.debug("fileCode {} ",fileCode );
                        handlerTagFileAndMaterialByCode(tagCode,fileCode,createBy);
                    } catch (IOException e) {
                        log.error("处理素材与标记文本 【{}】",e.getMessage());
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    /**
     * 通过文件在目录中code，处理素材与标记文本
     */
    private void handlerTagFileAndMaterialByCode(String targetCode,String fileCode,String createBy) throws IOException {
//        log.debug("处理素材与标记文本【{}】" ,fileCode);
        BaseMaterial tagMaterial = baseMaterialService.selectBaseMaterialById(targetCode);

        BaseFlieRecord tagFileRecord = baseFlieRecordService.selectBaseFlieRecordById(tagMaterial.getFileCode());
        BaseMaterial filMaterial = baseMaterialService.selectBaseMaterialById(fileCode);
//        log.debug("素材 {}" ,tagMaterial);
//        log.debug("标记文本 {}" ,tagFileRecord);
        filMaterial.setTabCode(tagMaterial.getFileCode());
//        baseMaterialService.updateBaseMaterial(filMaterial);
        List<BaseTagFile> readTagFile = readTagFile(getLocalPath(tagFileRecord.getPathFileName()),createBy,tagMaterial.getFileCode());//        log.debug("获取到的标记记录【{}】" ,readTagFile);
        saveTagFiletoDatabase(readTagFile);
        baseMaterialService.updateBaseMaterial(filMaterial);
    }

    /**
     * 深度遍历文件目录
     * @param sourceDirPath 目录
     * @param createBy 上传者
     * @param fileMap 目录与文件之间映射
     * @param tagMap 素材与标记文本之间映射
     * @return
     */
    private void DfsDirData(String sourceDirPath,String createBy ,Map<String, String> fileMap,Map<String,String> tagMap,Map<String,String>pathMap) throws IOException {
        log.debug("DFS 所在目录 【{}】" ,sourceDirPath);
        File dirFile = new File(sourceDirPath);
        // 获得传入路径下的所有文件
        File[] files = dirFile.listFiles();
        if (files != null){
            for (File file: files){
                if (file.isFile()){
//                    log.debug("加载文件 文件名称 [{}]" ,file.getName());
//                    log.debug("加载文件 文件所在目录 [{}]" ,sourceDirPath);
//                    log.debug("加载文件 文件路径 [{}]" ,file.getAbsolutePath());
                    String fileType = FileTypeEnum.getFileType(file.getName());
                    String accessPath = getAccessPath(file.getAbsolutePath());
                    String fileName = accessPath.substring(0,accessPath.lastIndexOf(".")) ;
                    String code = saveFileRecord(file,fileType,pathMap.get(getAccessPath(file.getParentFile().getAbsolutePath())),createBy,accessPath);
                    if (fileType != null && fileType.startsWith(FileConstant.TAG_TYPE)){
                        tagMap.put(fileName,code);
                    }else{
                        fileMap.put(fileName,code);
                    }

                }else{
                    String pcode = pathMap.get(getAccessPath(file.getParentFile().getAbsolutePath()));
                    String nextDirCode = insertFileRecord(file.getName(),FileConstant.FOLDER_TYPE,0L,pcode,createBy);

                    pathMap.put(getAccessPath(file.getAbsolutePath()),nextDirCode);
                    DfsDirData(file.getAbsolutePath(),createBy,fileMap,tagMap,pathMap);
                }
            }
        }
    }
    /**
     * 把物理地址转换成访问地址
     */
    public String getAccessPath(String localPath){
        int index = localPath.indexOf("upload");
        String path = "/" + localPath.substring(index,localPath.length());
        path = path.replaceAll("\\\\", "/");
        log.debug("文件访问路径【{}】 " ,path);
        return  path;
    }


    /**
     * 读取标记文本的内容
     *
     * @param sourceFilePath
     * @return 标记文本信息
     */
    private List<BaseTagFile> readTagFile(String sourceFilePath, String createBy,Long fileCode) throws IOException {
        log.debug("BaseUpAndLoadServiceImpl.readTagFile()  读取标记文本路径 [{}]", sourceFilePath);
        BufferedReader reader = null;
        String line = null;
        List<BaseTagFile> res = new ArrayList<>();
        File file = new File(sourceFilePath);
        try {
            reader = new BufferedReader(new FileReader(file));
            while ((line = reader.readLine()) != null) {
                //todo 读取文件为乱码
                log.debug("BaseUpAndLoadServiceImpl.readTagFile()  读取标记文本信息 [{}]", line);
                if (line == null || line == "") {
                    continue;
                }
                String[] data = line.split(" ");
                BaseTagFile tag = new BaseTagFile();
                tag.setTagCategory(Long.valueOf(data[0]));
                String tagInfo = "";
                for (int i = 1; i < data.length; i++) {
                    tagInfo += data[i] + " ";
                }
                tag.setTagInfo(tagInfo);
                tag.setCreateTime(new Date());
                tag.setFileCode(fileCode);
                tag.setCreateBy(createBy);
                res.add(tag);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return res;

    }

    /**
     * 把标记文本内容保存到数据库
     */
    private boolean saveTagFiletoDatabase(List<BaseTagFile> tags) {
        return baseTagFileService.addBaseTagFileList(tags) > 0 ? true : false;
    }



    /**
     * 检查文件的标签是否存在，并新增
     */
    private List<BaseLabel> checkLabelNames(String[] labels, String createBy) {
        List<BaseLabel> res = new ArrayList<>();
        for (String label : labels) {

            BaseLabel select = baseLabelService.selectBaseLabelByLabel(label);
            if (select == null) {
                select = new BaseLabel();
                select.setLabel(label);
                select.setCreateTime(DateUtils.getNowDate());
                select.setCreateBy(createBy);
                baseLabelService.insertBaseLabel(select);
            }
            res.add(select);
        }
        return res;
    }

}




